Python の独自パッケージを作成して AWS CodeArtifact に登録してみた
今回は、Python の勉強を兼ねて独自パッケージを作成して、AWS CodeArtifact に登録してみました。
作業の流れ
- AWS CodeArtifact のドメインを作成
- AWS CodeArtifact のリポジトリを作成
- ツールのインストール
- 独自パッケージの作成
- リポジトリへ独自パッケージを登録
- 動作確認
AWS CodeArtifact のドメイン作成
まずは クラウド側の環境構築を行います。AWS CodeArtifact のコンソールから「ドメインを作成」をクリックします。
次の画面でドメイン名を入力します。(今回は mydomain
としました)
AWS CodeArtifact のリポジトリを作成
次に、作成したドメインに対してリポジトリを作成します。
適当なリポジトリ名を入力して作成します。(今回は myrepo
としました)
なお、今回はパブリックアップストリームリポジトリは設定しません。
作成できました。
twine と wheel のインストール
AWS CodeArtifactへのアップロードは twine を使って行うので、twine を PC にインストールします。また、アップロード時は wheel パッケージである必要があるので、wheel もインストールしておきます。
% pip install -U twine % pip install -U wheel
独自パッケージの作成
作成するパッケージの概要
ここからは Python のパッケージを作成していきます。具体的には「AWSサービスに対して正しい名称を返してくれるもの」を作ります。動作イメージとしては下記のような感じです。名前は myawsname
としてみました。
>>> import myawsname >> myawsname.s3() { "ServiceName": "Amazon S3" } >>> myawsname.greengrass() { "ServiceName": "AWS IoT Greengrass" }
ファイル構成
パッケージ作成に必要なものを下記の構成で作成していきます。
作業ディレクトリ ├── LICENSE.txt ├── README.md ├── myawsname │ ├── __init__.py │ └── myawsname.py └── setup.py
モジュール作成
本体となるコード(myawsname/myawsname.py
)は下記のような簡単なものを用意します。
import json def common(message): data = json.loads(message) json_message = json.dumps(data, indent=2) return json_message def s3(): message = '{"ServiceName": "Amazon S3"}' return common(message) def dynamodb(): message = '{"ServiceName": "Amazon DynamoDB"}' return common(message) def greengrass(): message = '{"ServiceName": "AWS IoT Greengrass"}' return common(message)
setup.py
の作成
次に setup.py
を以下の内容で作成します。下記の公式ページにあるサンプルをベースに必要箇所を修正して作成してください。
import setuptools with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() setuptools.setup( name="myawsname", version="0.0.1", author="cm-taro", author_email="xxx@example.com", license='MIT', description="You can receive AWS Service Name.", long_description=long_description, long_description_content_type="text/markdown", url="https://example.com", classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], packages=setuptools.find_packages(), python_requires=">=3.7", )
ライセンスについては、11 行目と 18 行目の2箇所に記載します。
classifiers
の箇所だけでもいいですが、その場合は pip show
コマンドで確認時にライセンスが UNKNOWN
と表示されてしまいます。
「pip-licenses」 というパッケージを使うと classifiers
からライセンス情報を取得することができます。
今回は pip show
のときも正しく表示させるために、パッケージのメタデータとして license
フィールドも書いておきます。
また、url
はプロジェクトのホームページのものを記載することになりますが、今回は用意していないのでダミーの URL を記載しておきます。
LICENSE.txt
の作成
今回は MIT ライセンスにしてみたので、下記のテキストから作成します。
Copyright (c) 2022 Taro CM Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
__init__py
の作成
次にmyawsname/__init__.py
を作成します。このファイルのおかげでパッケージ利用時はimport myawsname
とすれば利用できるようになります。このファイルがない場合は、from myawsname import myawsname
とする必要があります。
from myawsname.myawsname import *
README.md
の作成
最後にREADME.md
を作成します。今回はテストなので中身は簡単なものにします。
# TITLE This is a sample package. # LICENSE This software is released under the MIT License, see LICENSE.
パッケージのビルド
準備ができたのでパッケージをビルドします。
$ python setup.py sdist
dist
ディレクトリおよびパッケージ名.egg-info
ディレクトリができていることを確認します。
. ├── LICENSE.txt ├── README.md ├── dist │ └── myawsname-0.0.1.tar.gz ├── myawsname │ ├── __init__.py │ └── myawsname.py ├── myawsname.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ └── top_level.txt └── setup.py
次に wheel パッケージのビルドを実行します。
$ python setup.py bdist_wheel
build
ディレクトリが作成されていることを確認します。
. ├── LICENSE.txt ├── README.md ├── build │ ├── bdist.macosx-10.15-x86_64 │ └── lib │ └── myawsname │ ├── __init__.py │ └── myawsname.py ├── dist │ ├── myawsname-0.0.1-py3-none-any.whl │ └── myawsname-0.0.1.tar.gz ├── myawsname │ ├── __init__.py │ ├── myawsname.py │ └── test.py ├── myawsname.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ └── top_level.txt └── setup.py
リポジトリへパッケージを登録
twine
を使ってアップロードを行っていきます。
最初に aws codeartifact login --tool twine
コマンドを実行します。
$ aws codeartifact login \ --tool twine \ --repository myrepo \ --domain mydomain
このコマンドを実行すると、~/.pypirc
ファイルが下記の内容で作成されます。
[distutils] index-servers = pypi codeartifact [codeartifact] repository = https://[DOMAIN]-[YOUR_AWS_ACCOUNT_ID].d.codeartifact.[REGION].amazonaws.com/pypi/[REPOSITROY]/ username = aws password = [YOUR_CODEARTIFACT_AUTH_TOKEN]
password
に指定されているトークンは CodeArtifact に接続するために必要な権限を持ちます。
また、この他にpip install
で CodeArtifact からパッケージをインストールする際には、aws codeartifact login --tool pip
コマンドを実行しますが、このときも同様のトークンを取得することができます。このときのトークンは、~/.config/pip/pip.conf
に書き込まれます。
「トークンの発行」と「各ファイルに対するトークンの書き込み、ファイル参照」のタイミングは次のとおりです。
ファイル | 書込タイミング | 参照タイミング |
---|---|---|
~/.config/pip/pip.conf |
・aws codeartifact login --tool pip 実行時 ・ aws codeartifact get-authorization-token で取得したトークンを pip config set で書き込んだ時 |
パッケージのインストール時 |
~/.pypirc |
aws codeartifact login --tool twine 実行時 |
パッケージのリポジトリへのアップロード時 |
上記の通り、各ファイルで CodeArtifact を利用するためのトークンを持ちますが、パッケージのインストールとアップロードは実施頻度やタイミングが全く異なるはずなので、各ファイルにあるトークンが全く別のものであっても問題ありません。(片方のトークンが有効期限切れでも問題ありません)
都度、必要な作業に応じてトークンを発行して(ファイルに書込んで、)利用する形になります。
少し話がそれましたが、次にアップロードを実行します。
$ twine upload -r codeartifact dist/*
コマンドを実行すると下記のようなログが表示されて、AWS CodeArtifact のリポジトリにアップロードされていることが分かります。
Uploading distributions to https://mydomain-xxxxxxxxxxx.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/myrepo/ Uploading myawsname-0.0.1-py3-none-any.whl 100%|██████████████████████████████████████████████████| 7.39k/7.39k [00:00<00:00, 15.8kB/s] Uploading myawsname-0.0.1.tar.gz 100%|██████████████████████████████████████████████████| 7.14k/7.14k [00:00<00:00, 21.3kB/s]
アップロード後、コンソール上でもアップロードしたパッケージ myawsname
が確認できました。
また、今回アップロードしたバージョン 0.0.1
のページを開くと setup.py
に記載した内容でパッケージ情報を見ることができます。
動作確認
アップロードできたので、実際に作成したパッケージをインストールして動作テストをしてみましょう。
CodeArtifact からインストールするために、最初に CodeArtifact に接続します。
AWS CLI でAWS CodeArtifact に接続することで、有効期限を持った認証トークンを含んだリポジトリの URL が生成されて ~/.config/pip/pip.conf
に書き込まれます。
このトークンの有効期間はデフォルトで12時間です。もっと短い時間を指定することもできます。
なお、接続手順はコンソールから確認することができます。
「接続手順の表示」 をクリックすると下記の様な画面になります。
今回は Python なので pip
を選択します。すると、実行するコマンドが表示されるのでコピーして手元の PC で実行します。
$ aws codeartifact login \ --tool pip \ --repository myrepo \ --domain mydomain \ --domain-owner [YOUR_AWS_ACCOUNT_ID]
コマンドを実行すると、~/.config/pip/pip.conf
に下記のような内容が書き込まれます。同じコマンドを実行するたびに新しいトークンを含む URL で上書きされます。
[global] index-url = https://aws:[YOUR_CODEARTIFACT_AUTH_TOKEN]@mydomain-[YOUR_AWS_ACCOUNT_ID].d.codeartifact.ap-northeast-1.amazonaws.com/pypi/myrepo/simple/
事前の準備が終わったので、パッケージをインストールします。
$ pip install myawsname
次のようなログが流れて、AWS CodeArtifact からインストールされていることが分かります。
Looking in indexes: https://aws:****@mydomain-xxxxxxxxxxxx.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/myrepo/simple/ Collecting myawsname Downloading https://mydomain-xxxxxxxxxxxx.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/myrepo/simple/myawsname/0.0.1/myawsname-0.0.1-py3-none-any.whl (2.9 kB) Installing collected packages: myawsname Successfully installed myawsname-0.0.1
今回の手順では、pip.conf
の設定どおりデフォルトのリポジトリは CodeArtifact になっています。そのためpip install
が失敗する場合は、トークンの有効期限が切れている可能性があります。
その場合は、再度 AWS CodeArtifact にログインし直して、新しいトークンを取得してください。
aws codeartifact login --tool pip
コマンドを実行することで、新しいトークンを含んだ URL で~/.config/pip/pip.conf
を更新してくれます。
パッケージのインストールができたら、次のような確認用のコードで検証してみます。
import myawsname import json s3_name_data = json.loads(myawsname.s3()) s3_name = s3_name_data['ServiceName'] print("s3: " + str(s3_name))
以下のような実行結果になればOKです。
$ python test.py s3: Amazon S3
シェル上でも確認できました。
$ python Python 3.8.7 (default, Jul 8 2021, 15:28:23) [Clang 12.0.0 (clang-1200.0.32.29)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import myawsname >>> myawsname.s3() '{"ServiceName": "Amazon S3"}' >>> >>> myawsname.dynamodb() '{"ServiceName": "Amazon DynamoDB"}' >>> >>> myawsname.greengrass() '{"ServiceName": "AWS IoT Greengrass"}' >>>
pip
コマンドでもパッケージ情報を確認してみます。
$ pip show myawsname Name: myawsname Version: 0.0.1 Summary: You can receive AWS Service Name. Home-page: https://example.com Author: cm-taro Author-email: xxx@example.com License: MIT Location: /Users/xxx/.pyenv/versions/3.8.7/lib/python3.8/site-packages Requires: Required-by:
最後に
Python のパッケージの作成方法と AWS CodeArtifact の基本的な使い方を確認できました。 引き続き AWS CodeArtifact を触ってみたいと思います。
以上です。